package storage;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;

import java.util.*;

/**
 * Developed for NTT - All is an Entity
 * Questa classe gestisce un pool di connessioni al database.
 * 
 * @author Alessandro Pollace
 * 
 * @version 0.1
 */
public class ConnectionPool {

    /***** DATA *****/
    /**
     * connessioni
     */
    private static Connection Pool[];
    /**
     * configurazione del database
     */
    private static Properties conf;
    /**
     * numero di Connessioni attualmente rilasciate e in attesa di riacquisizione
     */
    private static int outConnection; 
    /**
     * numero di Connessioni attualmente in attesa di essere rilasciate
     */
    private static int inConnection;
    /**
     * numero di connessioni totali concesse
     */
    private static int maxConnection;

    /************************************************************************/
    /***** CONSTRUCTOR *****/
    /**
     * Crea una nuova istanza dell'oggetto ConnectionPool correttamente
     * configurata
     *
     * @throws Exception
     *             File di configurazione non trovato
     */
    public ConnectionPool() throws Exception {
        getConfigurationDB();
    }

    /************************************************************************/
    /***** METHOD - PRIVATE *****/
    /**
     * Legge il gile di configurazione db.conf situato nella cartella conf.
     *
     * @throws Exception
     */
    private static synchronized void getConfigurationDB() throws Exception {
        // DATA
        if (Pool == null) {
            ConfigLoader configL = new ConfigLoader();
            InputStream fileConf;
            conf = new Properties();

            // OPERATION
            try {
                fileConf = new FileInputStream(configL.getRoot() + "conf/db.conf");
            } catch (FileNotFoundException e) {
                // ERROR
                throw new Exception(
                        "Error-ConnectionPool-Constructor-ConfFileNotFound");
            }
            try {
                conf.load(fileConf);
            } catch (IOException e) {
                System.out.println(e.getMessage());
                throw new Exception(
                        "Error-ConnectionPool-Constructor-ConfFileNotLoad");
            }
            maxConnection = Integer.parseInt(conf.getProperty("MaxConnectionInCache"));
            Pool = new Connection[maxConnection];
        }

    }

    /**
     * Crea una nuova connessione partendo dalla configurazione del database
     * precedentemente caricata
     *
     * @return Nuova connessione
     * @throws Exception
     *             Errore Database Non trovato
     */
    private Connection getNewConnection() throws Exception {
        ConfigLoader configL = new ConfigLoader();
        Connection c = null;
        if (conf.getProperty("DBMS").equals("SQLite") == true) {
            try {
                Class.forName("org.sqlite.JDBC");
            } catch (Exception e) {
                throw new Exception(
                        "Error-ConnectionPool-getNewConnection-DriverSQLiteNotFound");
            }
            try {
                c = DriverManager.getConnection("jdbc:sqlite:"
                        + configL.getRoot() + conf.getProperty("dbLocation"));
            } catch (Exception e) {
                // ERROR
                throw new Exception(
                        "Error-ConnectionPool-getNewConnection-SQLite:DBFileNotFound");
            }
        }
        return c;
    }

    /************************************************************************/
    /***** METHOD - PROTECTED *****/
    /**
     * Questo metodo ritorna una connessione libera e se non ne esiste una, ne
     * crea una nuova al momento.
     *
     * @return Connessione al database
     * @throws Exception
     *             fuori del numero massimo di connessioni consentite
     */
    protected synchronized Connection getConnection() throws Exception {
        if (outConnection == maxConnection) {
            throw new Exception(
                    "Error-ConnectionPool-getConnection-OverMaxNumberOfConnection");
        }
        if (inConnection == 0) {
            Pool[0] = this.getNewConnection();
            inConnection++;
        }
        outConnection++;
        inConnection--;
        return Pool[inConnection];
    }

    /**
     * Questo metodo riacquista una connessione rilasciata in precedenza.
     *
     * @param c
     *            connessione da restituire
     * @throws Exception
     *             Errore nella chiusura del database
     */
    protected synchronized void relaseConnection(Connection c) throws Exception {
        outConnection--;
        Pool[inConnection] = c;
        inConnection++;
    }
    /************************************************************************/
}
